package aliasMatching.controller;

import aliasMatching.model.DisplayInfo;
import aliasMatching.model.MyTableModel;
import java.util.Arrays;
import java.util.List;

/**
 * Implementation of Jaro-Winkler for comparing alias names. To a large extent 
 * the code is taken from LingPipe.
 * @author frejoh
 *
 */
public class NameEquality {

    private double mWeightThreshold = 0.0;
    private int mNumChars = 0;
    public static final NameEquality JARO_DISTANCE = new NameEquality();
    // public static final NameEquality JARO_WINKLER_DISTANCE = new NameEquality(0.70,4); // The default as used in Winkler's papers
    List username;
    List userList;

    /**
     * Construct a basic Jaro instance (i.e. without the Winkler modifications)
     */
    public NameEquality() {
        //this(Double.POSITIVE_INFINITY,0);
    }

    /**
     * Construct Winkler-modified Jaro string
     * @param weightThreshold
     * @param numChars
     */
    public NameEquality(double weightThreshold, int numChars,
            List userListID, List username, MyTableModel tableMdl) {
        mNumChars = numChars;
        mWeightThreshold = weightThreshold;
        this.username = username;
        this.userList = userListID;
        for (int x = 1; x < userList.size(); x++) {
            //for (int y = x + 1; y < userList.size(); y++) {
                float proximity = (proximity(username.get(0).toString(),
                        username.get(x).toString()));
                DisplayInfo di = new DisplayInfo(username.get(0).toString(),
                        userList.get(x).toString(),
                        username.get(x).toString(),
                        Double.toString(proximity),
                        proximity * 100,
                        "Name");
                tableMdl.SetValue(di);
          //  }
        }

    }

    public float distance(String cSeq1, String cSeq2) {
        return 1.0f - proximity(cSeq1, cSeq2);
    }

    public float proximity(String cSeq1, String cSeq2) {
        int len1 = cSeq1.length();
        int len2 = cSeq2.length();
        if (len1 == 0) {
            return len2 == 0 ? 1.0f : 0.0f;
        }

        int searchRange = Math.max(0, Math.max(len1, len2) / 2 - 1);

        boolean[] matched1 = new boolean[len1];
        Arrays.fill(matched1, false);
        boolean[] matched2 = new boolean[len2];
        Arrays.fill(matched2, false);

        int numCommon = 0;
        for (int i = 0; i < len1; ++i) {
            int start = Math.max(0, i - searchRange);
            int end = Math.min(i + searchRange + 1, len2);
            for (int j = start; j < end; ++j) {
                if (matched2[j]) {
                    continue;
                }
                if (cSeq1.charAt(i) != cSeq2.charAt(j)) {
                    continue;
                }
                matched1[i] = true;
                matched2[j] = true;
                ++numCommon;
                break;
            }
        }
        if (numCommon == 0) {
            return 0.0f;
        }

        int numHalfTransposed = 0;
        int j = 0;
        for (int i = 0; i < len1; ++i) {
            if (!matched1[i]) {
                continue;
            }
            while (!matched2[j]) {
                ++j;
            }
            if (cSeq1.charAt(i) != cSeq2.charAt(j)) {
                ++numHalfTransposed;
            }
            ++j;
        }
        // System.out.println("numHalfTransposed=" + numHalfTransposed);
        int numTransposed = numHalfTransposed / 2;

        // System.out.println("numCommon=" + numCommon
        // + " numTransposed=" + numTransposed);
        float numCommonD = numCommon;
        float weight = (numCommonD / len1
                + numCommonD / len2
                + (numCommon - numTransposed) / numCommonD) / 3.0f;

        if (weight <= mWeightThreshold) {
            return weight;
        }
        int max = Math.min(mNumChars, Math.min(cSeq1.length(), cSeq2.length()));
        int pos = 0;
        while (pos < max && cSeq1.charAt(pos) == cSeq2.charAt(pos)) {
            ++pos;
        }
        if (pos == 0) {
            return weight;
        }
        return weight + 0.1f * pos * (1.0f - weight);

    }
}
